In ODF release 1 views can be created either by program or in resources. This document describes how to define your views in resources, using the predefined resource types or after creating new types.
See also the folder AppMaker in Tools & Goodies which provides information on the first 3rd party visual tool that can generate ODF view resources. Please check our web page at /www.devtools.apple.com/odf/ for the latest information on AppMaker and other tools.
Typical Views.fr Resource File
All ODF samples using views have a project file Views.fr containing the resource definitions. You can have more than one resource file in your project, as long as the views of each frame are defined in the same file. ODF cross-platform resource compiler, ODFRC, compiles text resources into binary data in your part. This data is loaded at runtime when each frame is displayed, either automatically during FW_CFrame::FacetAdded if you provided a resource id in your frame's constructor, or manually when you call CreateSubViewsFromResource(ev, resourceId).
A typical Views.fr starts by including these headers:
#ifndef FWRESFIL_K
#include "FWResFil.k" // some predefined ODFRC types
#endif
#ifndef FWVIEWS_FR
#include "FWViews.fr" // predefined View resource types and macros
#endif
#ifndef FWNOTDEF_H
#include "FWNotDef.h" // Notification constants
#endif
#ifndef DEFINES_K
#include "Defines.k" // Your part's constants
#endif
FWViews.fr is the main file to look at for the current resource templates. It includes FWViews.k containing most view constants and also defines some handy macros. It is convenient to use #define values in your own file to represent coordinates and fonts, as in Form's View.fr:
If your frame uses a custom view class you must define a corresponding resource type, before the frame's resource definition. See later in this document how to define new resource types.
type RFormView : FW_RSuperView(Label='Frmv')
{
// This form uses 3 radio-clusters (not included in FW_RSuperView)
FW_RRadioCluster;
FW_RRadioCluster;
FW_RRadioCluster;
};
Finally the frame's views are listed in a FW_RFrameLayout resource (the resource id kFormView is the same one used to create the frame instance in the part's NewFrame method):
resource FW_RFrameLayout(kFormView)
{
{H,V}, // LayoutSize
{ // Start list of frame's subviews
RFormView
(
...
}
View Resource Types
Frame Layout
ODF loads only one resource type automatically, FW_RFrameLayout, i.e. this can be the only top-level resource in your file unless you add some code to load other resources manually (see the last section of this document for more information on loading your own resources).
FW_RFrameLayout represents the layout of the whole view hierarchy inside a frame plus an optional scroller. ODF loads this resource the first time a facet is created for that frame. The frame instance itself was created in your part's NewFrame method, passing the resource id to the FW_CFrame constructor. At the time OpenDoc needs to display the frame its method FacetAdded is called, and if this is the first facet, this calls in turn CreateSubViewsFromResource(ev, resourceId).
Note: if your frame wasn't created with a resource id the method CreateSubViews is called instead, where you can either load the view resources manually with CreateSubViewsFromResource or create them by program.
FW_RFrameLayout starts with a point representing the layout size followed by the list of subviews. For example:
resource FW_RFrameLayout(kFormView)
{
{H,V}, // LayoutSize (i.e. virtual extent)
{ // ----------------------> Start list of frame's subviews
}, // ----------------------> End list of frame's subviews
{ // 1 of 0 scroller
FW_RScrollBarScroller
(
0, // AutoScrollInset: not used here
{0, 0} // AutoScrollIncrement: 0 = no auto-scroll
kHorzScrollBarID, // Horizontal scroll-bar ID
kVertScrollBarID // Vertical scroll-bar ID
)
}
};
The layout size {H,V} is like a virtual extent (not the actual extent of the frame created from this resource!). Its value is not important, it is only used as a reference for the subviews defined below. For instance the scroll bars and grow box are placed at the edges of this layout, with the correct 1 pixel overlap. When the resource is loaded at runtime the layout size is replaced by the actual extent of the frame which has the effect of adjusting each subview's position and size according to their bindings.
After the list of subviews comes a list containing 0 or 1 scroller, depending if this frame's content view scrolls or not.
Content View Resource
If your frame doesn't contain a separate content view its layout resource is usually a one-level hierarchy composed of simple views, see for example the dialog resource kPasswordDialog in Form's Views.fr. On the other hand having a separate content view requires using a subtype of FW_RSuperView because the content view is a subclass of FW_CSuperView. See the section below, "Adding a New Resource Type".
Other View Resources
Each class of views is described in a separate engineering note in the same folder. See these documents for more information on resources for Controls, Edit Views, List Boxes, etc. The file FWViews.fr also contains comments describing the fields of each resource type.
Adjusting the Frame and its SubViews
Everything you need to create your frame's view hierarchy can be defined in a single FW_RFrameLayout resource, except for the frame's position and size that are set by the container part or the window. You can also make additional modifications right after loading the resource in your frame's PostCreateViewFromStream method. Here is a list of things that can be done in this method:
• Create objects that don't have any resource types, such as a FW_CViewTabber.
• Remove scroll bars when the frame is not the root frame.
• Connect notifiers other than controls to your frame.
• Disable controls.
• More generally, perform any kind of initialization on the newly created views.
Once loaded any view can be accessed through its view id, like this:
Note: while your part is in development you may want to use RTTI instead of a direct cast to be able to catch errors on the view ids.
Adding a New Resource Type (Making Your Class Archivable)
Every time you subclass a view class and want to store its instances in your frame's layout resource you need to define a new resource type and make your class archivable. This happens in two typical cases: your frame has a separate content view (it is always a subclass of FW_CSuperView) or you want to customize one of the simple views that are not superviews.
Note: for more information on the syntax used in resource files see the ODFRC Reference Manual in the Documentation folder and the various types defined in FWViews.fr.
Making Your Class Archivable
In order to load a class from resources you need to declare it to the ODF Archiver. This is done by using the macro FW_REGISTER_ARCHIVABLE_CLASS in the source of this class and implementing the following methods: Create, Destroy, Flatten and InitializeFromStream.
FW_REGISTER_ARCHIVABLE_CLASS is described in the Streams Subsystem API Reference. For example the Container sample registers its content view class, CContainerView, at the top of View.cpp as follows:
LContainerView is the class constant also used in the resource type definition. It must be unique among all class labels registered with the Archiver (ODF reserves labels which are all lowercase letters or digits, so, to avoid conflict, your label should contain at least an uppercase letter or another character).
• CContainerView::Create is a static method for creating the object during a two-step input.
• CContainerView::Destroy is a static method for deleting the object in case the initialization fails during a two-step input.
• FW_CView::Read and FW_CView::Write are static methods of FW_CView for initializing and outputing any view.
As the following code sample shows, Create is a simple call to the class minimal constructor and Destroy is a simple delete call.
Once the object is created with Create, the Archiver reads the stream with FW_CView::Read which in turn calls your class virtual method InitializeFromStream (2 steps initialization). InitializeFromStream should call inherited first and then perform additional intializations for this class. The same way, when outputing the object, the Archiver calls FW_CView::Write which in turn calls your class virtual method Flatten.
Adding a Content View Resource
As mentioned earlier having a separate content view requires using a subtype of FW_RSuperView. A simple content view such as the one in Container doesn't add any field to its base type, the definition in Views.fr is straightforward, the only addition is the class label 'ctvw' that was used in the macro FW_REGISTER_ARCHIVABLE_CLASS.
type RContainerView : FW_RSuperView(Label='ctvw')
{
};
The initialization of the class is a simple call to the base class to read in the resource. In this example InitializeFromStream contains additional code to get pointers to the frame itself, to the part and to the content (here the frame could be accessed more easily with GetFrame(ev) because the hierarchy is already built at this point).
Note: when your content view doesn't add any field and your InitializeFromStream or Flatten methods just call the base class you don't need to implement them of course. We left them in our samples for your convenience since you may use this code as a starting point.
In the case of Form the content view type adds 3 custom fields representing the 3 radio cluster objects:
type RFormView : FW_RSuperView(Label='Frmv')
{
// This form uses 3 radio-clusters (not included in FW_RSuperView)
// The code to load them is in CFormView::InitializeFromStream().
FW_RRadioCluster;
FW_RRadioCluster;
FW_RRadioCluster;
};
So the InitializeFromStream method must read this data from the stream after calling the base class:
Note: CFormView::Flatten is not completed yet, it should stream out the 3 cluster objects after the superview resource.
Adding Other View Resources
You need to follow a similar implementation for defining other view resource types, for instance when subclassing light views like controls or edit views. Form contains a custom edit view whose resource types adds 3 fields to the default type:
type RScrollEdit : FW_REditView(Label='Sedv')
{
HorizViewID:
longint; // Horizontal scrollbar id, or 0 for no scrollbar
VertViewID:
longint; // Vertical scrollbar id, or 0 for no scrollbar
TextWidth:
FW_RFixed; // text width (or 0 to use same width as view)
};
The initialization method reads the resource stream as follows:
You can create the subview hierarchy of an existing superview by loading its FW_RViewLayout resource: this is the base type for FW_RFrameLayout, it contains the same virtual extent point and list of subviews, but no scroller field.